package yartiss.engine.simulation.scheduling.policies.fp;

import java.util.SortedSet;

import yartiss.engine.simulation.Event;
import yartiss.engine.simulation.IEnergyProfile;
import yartiss.engine.simulation.ISchedulingPolicy;
import yartiss.engine.simulation.events.EventGenerator;
import yartiss.engine.simulation.scheduling.policies.AbstractMonoProcessorSchedulingPolicy;
import yartiss.engine.simulation.scheduling.policies.tools.FPSlackEnergyManager1;
import yartiss.engine.simulation.scheduling.policies.tools.FPSlackTimeManager;
import yartiss.engine.simulation.system.ITask;
import yartiss.engine.simulation.system.ITaskSet;
import yartiss.engine.simulation.system.taskset.TaskSetFactory;

public class FPegSchedulingPolicy extends AbstractMonoProcessorSchedulingPolicy {

	private final FPSlackTimeManager slackTimeManager;
	private final FPSlackEnergyManager1 slackEnergyManager;

	public FPegSchedulingPolicy(FPSlackTimeManager slackTimeManager, FPSlackEnergyManager1 slackEnergyManager) {
		this.slackTimeManager = slackTimeManager;
		this.slackEnergyManager = slackEnergyManager;
	}

	@Override
	public String getPolicyName() {
		return "FPeg";
	}

	@Override
	public ITaskSet createTaskSet() {
		return TaskSetFactory.getNewInstance("priority");
	}
	
	@Override
	public ISchedulingPolicy newInstance() {
		return new FPegSchedulingPolicy(new FPSlackTimeManager(), new FPSlackEnergyManager1());
	}

	// private long wakeup = -1;
	// private long lastdate = -1;
	public static boolean log = false;
	// private boolean slackTimeComputed = false;
	// private long st = 0;

	boolean isIdle = false;
	long idleDate = Long.MAX_VALUE;

	@Override
	public ITask chooseNextTask(ITaskSet taskSet, IEnergyProfile energyProfile, long date, EventGenerator evGen) {
		metrics.clear();
		SortedSet<ITask> activeTasks = taskSet.getActiveTasks(date);
		if (activeTasks.isEmpty()) {

			if (log)
				System.err.println("enter activeTask empty condition");

			isIdle = true;
			idleDate = Math.min(date, idleDate);

			return null;
		}

		ITask first = activeTasks.first();

		long de = energyProfile.getMaxEnergyLevel() - energyProfile.getCurrentEnergyLevel();
		long dt = de / energyProfile.getPowerRate();

		if (dt <= 0) {
			isIdle = false;
			idleDate = Long.MAX_VALUE;

			if (log)
				System.err.println("negative dt, forcing isIdle to false");

		}

		if (!isIdle || idleDate == date) {

			if (log)
				System.err.println("isIdle is false or idleDate is current date : " + isIdle + " " + idleDate + " ?= " + date);

			long se = (dt > 0) ? slackEnergyManager.getSlackEnergy(date, taskSet, energyProfile) : emaxSpecialCase(date, taskSet, energyProfile);

			metrics.put(Event.SlackEnergy, se);

			if (log)
				System.err.println(date + ": se= " + se);

			if (se >= first.getEnergyConsumptionRate()) {
				long tb = energyProfile.howLongCanExecute(se,first);

				tb = Math.min(tb, first.getRemainingCost());

				if (log)
					System.err.println("se is >= to first (" + first.getEnergyConsumptionRate() + ")");

				return execute(first, tb, energyProfile, date, evGen);
			}

			assert dt > 0 : " GNEEEE ?";

			long st = slackTimeManager.getSlackTime(date, taskSet);
			metrics.put(Event.SlackTime, st);

			if (log)
				System.err.println(date + ": st= " + st);

			if (st > 0) {
				isIdle = true;
				idleDate = Math.min(date, idleDate);

				assert dt > 0 && st > 0 : "aie";

				evGen.generateEvent("check_energy_state", first, date + Math.min(dt, st), null);
				return null;
			}

			long tb = energyProfile.howLongCanExecute(first);
			if (tb > 0)
				return execute(first, 1, energyProfile, date, evGen);

			isIdle = true;
			idleDate = Math.min(date, idleDate);
			return null;

		}

		assert dt > 0 : " GNEEEE 2  ?";

		long st = slackTimeManager.getSlackTime(date, taskSet);
		metrics.put(Event.SlackTime, st);

		if (log)
			System.err.println(date + ": st = " + st);

		if (st > 0) {

			assert dt > 0 && st > 0 : "here";

			evGen.generateEvent("check_energy_state", first, date + Math.min(dt, st), null);
			return null;
		}
		isIdle = false;
		idleDate = Long.MAX_VALUE;
		return chooseNextTask(taskSet, energyProfile, date,evGen);

	}

	private long emaxSpecialCase(long date, ITaskSet taskSet, IEnergyProfile energyProfile) {

		// long se = slackEnergyManager.getSlackEnergy(date, taskSet,
		// energyProfile);
		//
		// System.err.println("E~=Emax ! -> " + se);
		return taskSet.getActiveTasks(date).first().getEnergyConsumptionRate();

	}

	private ITask execute(ITask first, long timeBudget, IEnergyProfile energyProfile, long date, EventGenerator evGen) {

		if (log)
			System.err.println(first + "will be executed with tb = " + timeBudget);

		if (timeBudget <= 0) {
			isIdle = true;
			idleDate = Math.min(date, idleDate);
			evGen.generateEvent("check_energy_state", first, date + 1, null);
			return null;
		}

		evGen.generateEvent("check_energy_state", first, date + timeBudget, null);

		isIdle = false;
		idleDate = Long.MAX_VALUE;

		return first;
	}

	// SortedSet<Task> activeTasks = taskSet.getActiveTasks(date);
	// if (!activeTasks.isEmpty()) {
	//
	// Task first = activeTasks.first();
	// long hlcet = energyProfile.howLongCanExecute(first
	// .getEnergyConsumptionRate());
	// if (hlcet <= 0) {
	// EventGenerator.generateEvent("check_energy_state", first,
	// date + 1, null);
	// return null;
	// }
	//
	//
	//
	// long se = slackEnergyManager.getSlackEnergy(date, taskSet,
	// energyProfile);
	// // System.out.println(date+ "se="+se);
	//
	// if (se >= first.getEnergyConsumptionRate()) {
	//
	// // long wakeUp = -1;
	// //
	// // if (hlcet * first.getEnergyConsumptionRate() <= se) {
	// // if (hlcet < first.getRemainingCost())
	// // wakeUp=hlcet;
	// //
	// // } else
	// // wakeUp = Math.min(se /
	// // first.getEnergyConsumptionRate(),hlcet);
	// //
	// //
	// // if(wakeUp!=-1)
	//
	// long wakeUp = 1;
	// EventGenerator.generateEvent("check_energy_state", first, date
	// + wakeUp, null);
	// return first;
	// }
	//
	//
	// if (energyProfile.getCurrentEnergyLevel()
	// + energyProfile.getPowerRate()
	// > energyProfile
	// .getMaxEnergyLevel()) {
	//
	// // PATCH Inutile si Slack E bien calcule MAIS PERMET DE FAIRE AUSSI BIEN
	// QUE EHP4
	//
	// long wakeUp = 1;
	// EventGenerator.generateEvent("check_energy_state", first, date
	// + wakeUp, null);
	// return first;
	//
	//
	// }
	//
	//
	// EventGenerator.generateEvent("check_energy_state", first, date + 1,
	// null);
	// }
	// return null;
	//
	// // if (date != lastdate && wakeup > 0 && date < wakeup)
	// // return sleep(taskSet, energyProfile, date);
	// //
	// // lastdate = date;
	// // wakeup = -1;
	// //
	// // metrics.clear();
	// // slackTimeComputed = false;
	// //
	// // SortedSet<Task> activeTasks = taskSet.getActiveTasks(date);
	// //
	// // if (taskSet.getActiveTasks(date).isEmpty()) {
	// // return sleep(taskSet, energyProfile, date);// ne marche pas car vide
	// // avant la fin de la tache ???
	// // //System.out.println(date+ " : "+ null);
	// // //return null;
	// // }
	// //
	// //
	// //
	// // boolean isSlackEnergyDefined =
	// // slackEnergyManager.isSlackEnergyDefined( date, taskSet, activeTasks);
	// //
	// // if (energyProfile.getCurrentEnergyLevel() >
	// // energyProfile.getMinEnergyLevel()) {
	// //
	// // long se = isSlackEnergyDefined ? slackEnergyManager.getSlackEnergy(
	// // date, taskSet, energyProfile) : Long.MAX_VALUE;
	// // if(log)System.out.println(date
	// // +" : se="+se+" t"+activeTasks.first().getPriority());
	// //
	// // metrics.put(Event.SlackEnergy, se);
	// // if (se >= 0) // if there is slack energy, then execute
	// // return execute(taskSet, energyProfile, date);
	// // }
	// //
	// // return sleep(taskSet, energyProfile, date);
	//
	// }

	// private Task sleep(ITaskSet taskSet, IEnergyProfile energyProfile, long
	// date) {
	// // if (taskSet.getActiveTasks(date).isEmpty()) {
	// // long nextXi = Long.MAX_VALUE;
	// // for (Task t : taskSet)
	// // nextXi = Math.min(t.getNextActivation(date), nextXi);
	// // wakeup=nextXi;
	// // if(wakeup > date)
	// // EventGenerator.generateEvent("check_energy_state",
	// // taskSet.iterator().next(), wakeup, null);
	// // System.out.println(date+" : sleep until "+wakeup);
	// //
	// // return null;
	// // }
	//
	// st = slackTimeManager.getSlackTime(date, taskSet);
	// slackTimeComputed = true;
	// metrics.put(Event.SlackTime, st);
	//
	// if (st > 0) {
	//
	// long de = energyProfile.getMaxEnergyLevel()
	// - energyProfile.getCurrentEnergyLevel();
	// long dt = de / energyProfile.getPowerRate();
	//
	// if (dt > 0) {
	// wakeup = date + Math.min(st, dt);
	// EventGenerator.generateEvent("check_energy_state", taskSet
	// .iterator().next(), wakeup, null);
	// if (log)
	// System.out.println(date + " sleep to " + wakeup);
	// return null;
	// }
	//
	// SortedSet<Task> activeTasks = taskSet.getActiveTasks(date);
	//
	// boolean isSlackEnergyDefined = slackEnergyManager
	// .isSlackEnergyDefined(date, taskSet, activeTasks);
	// long se = isSlackEnergyDefined ? slackEnergyManager.getSlackEnergy(
	// date, taskSet, energyProfile) : Long.MAX_VALUE;
	//
	// long nextXi = Long.MAX_VALUE;
	// if (taskSet.getActiveTasks(date).isEmpty()) {
	// for (Task t : taskSet) {
	// long xi = t.isActivationTime(date) ? date : t
	// .getNextActivation(date);
	// nextXi = Math.min(xi, nextXi);
	// }
	// } else {
	// for (Task t : taskSet.getHeadSet(activeTasks.first(), false)) {
	// long xi = t.isActivationTime(date) ? date : t
	// .getNextActivation(date);
	// nextXi = Math.min(xi, nextXi);
	// }
	// }
	//
	// long energyWast = energyProfile.getPowerRate() * (nextXi - date);
	// // se -= energyWast;
	// metrics.put(Event.SlackEnergy, se);
	//
	// if (se < 0) {
	// wakeup = nextXi;
	// if (log)
	// System.out.println(date + " sleep to xi=" + wakeup + " se="
	// + (se + energyWast));
	// return null;
	// }
	//
	// }
	// fw = true;
	// if (log)
	// System.out.println(date + " sleep execute");
	// return execute(taskSet, energyProfile, date);
	//
	// }
	//
	// boolean fw = false;
	//
	// private Task execute(ITaskSet taskSet, IEnergyProfile energyProfile,
	// long date) {
	// SortedSet<Task> activeTasks = taskSet.getActiveTasks(date);
	// if (activeTasks.isEmpty())
	// return null;
	// Task higherPriorityTask_t = activeTasks.first();
	//
	// long hlcet = energyProfile.howLongCanExecute(higherPriorityTask_t
	// .getEnergyConsumptionRate());
	// if (hlcet <= 0) {
	// if (fw) {
	// fw = false;
	// return null;
	// }
	// return sleep(taskSet, energyProfile, date);
	// }
	// if (hlcet < activeTasks.first().getRemainingCost())
	// EventGenerator.generateEvent("check_energy_state",
	// higherPriorityTask_t, date + hlcet, null);
	//
	// return higherPriorityTask_t;
	//
	// }

	// public Task chooseNextTaskold(ITaskSet taskSet,
	// IEnergyProfile energyProfile, long date) {
	// metrics.clear();
	// slackTimeComputed = false;
	// if (date != lastdate && wakeup > 0 && date < wakeup)
	// return null;
	// lastdate = date;
	// wakeup = -1;
	// SortedSet<Task> activeTasks = taskSet.getActiveTasks(date);
	// if (!activeTasks.isEmpty()) {
	// Task higherPriorityTask_t = activeTasks.first();
	// boolean isSlackEnergyDefined = slackEnergyManager
	// .isSlackEnergyDefined(date, taskSet, activeTasks);
	// // System.out.println(date+" : se defined ="+isSlackEnergyDefined);
	// // slck energy defined
	// if (energyProfile.getCurrentEnergyLevel() > energyProfile
	// .getMinEnergyLevel() && isSlackEnergyDefined) {
	// long se = slackEnergyManager.getSlackEnergy(date, taskSet,
	// energyProfile);
	// // System.out.println(date +" : se="+se);
	// metrics.put(Event.SlackEnergy, se);
	//
	// if (se >= 0) {
	// long hlcet = energyProfile
	// .howLongCanExecute(higherPriorityTask_t
	// .getEnergyConsumptionRate());
	// if (hlcet <= 0) {
	// return sleep(taskSet, taskSet.iterator().next(), date,
	// energyProfile);// null;
	// }
	// if (hlcet < activeTasks.first().getRemainingCost())// ne
	// // devrait
	// // pas
	// // arriver
	// // :
	// // faire
	// // un
	// // assert
	// // pour
	// // verifier
	// EventGenerator.generateEvent("check_energy_state",
	// higherPriorityTask_t, date + hlcet, null);
	//
	// return higherPriorityTask_t;
	// }
	// st = slackTimeManager.getSlackTime(date, taskSet);
	// slackTimeComputed = true;
	//
	// // long de = energyProfile.getMaxEnergyLevel() -
	// // energyProfile.getCurrentEnergyLevel();
	// // long dt = de / energyProfile.getPowerRate();
	//
	// // FAUX : long dt = (long)Math.ceil(de /
	// // (double)energyProfile.getPowerRate());
	//
	// // System.out.println("dt="+dt);
	// // dt = Math.max(dt, 1);// TODO Verifier ca
	//
	// // NON
	// // if(dt==0 && se-energyProfile.getPowerRate() <
	// // higherPriorityTask_t.getEnergyConsumptionRate()) dt++;
	// metrics.put(Event.SlackTime, st);
	//
	// if (st > 0) {
	//
	// return sleep(taskSet, taskSet.iterator().next(), date,
	// energyProfile);
	// // wakeup = date + Math.min(st, dt);
	// // if(log)System.out.println("wakeup1 : "+wakeup);
	// // EventGenerator.generateEvent("check_energy_state",
	// // activeTasks.first(), wakeup, null);
	// // return null;
	// }
	// long hlcet = energyProfile
	// .howLongCanExecute(higherPriorityTask_t
	// .getEnergyConsumptionRate());
	// if (hlcet <= 0) {
	// return sleep(taskSet, taskSet.iterator().next(), date,
	// energyProfile);// null;
	// }
	// if (hlcet < activeTasks.first().getRemainingCost())
	// EventGenerator.generateEvent("check_energy_state",
	// higherPriorityTask_t, date + hlcet, null);
	//
	// return higherPriorityTask_t;
	// }
	// // slack energy undefined
	// long hlcet = energyProfile.howLongCanExecute(higherPriorityTask_t
	// .getEnergyConsumptionRate());
	// if (hlcet <= 0) {
	// if (!slackTimeComputed) {
	// st = slackTimeManager.getSlackTime(date, taskSet);
	// slackTimeComputed = true;
	// }
	// metrics.put(Event.SlackTime, st);
	// st = Math.max(1, st);
	// if (energyProfile.getCurrentEnergyLevel() < energyProfile
	// .getMaxEnergyLevel()) {
	// long de = energyProfile.getMaxEnergyLevel()
	// - energyProfile.getCurrentEnergyLevel();
	// long dt = de / energyProfile.getPowerRate();
	// st = Math.min(st, dt);
	// wakeup = date + st;
	// if (log)
	// System.out.println("wakeup2 : " + wakeup);
	// if (wakeup > date)
	// EventGenerator.generateEvent("check_energy_state",
	// activeTasks.first(), wakeup, null);
	// }
	// return sleep(taskSet, taskSet.iterator().next(), date,
	// energyProfile);// null
	// }
	// if (hlcet < activeTasks.first().getRemainingCost()) {
	// EventGenerator.generateEvent("check_energy_state",
	// activeTasks.first(), date + hlcet, null);
	// }
	// return activeTasks.first();
	// }
	// return sleep(taskSet, taskSet.iterator().next(), date, energyProfile);//
	// null
	// }
	//
	// private Task sleep(ITaskSet taskset, Task task, long date,
	// IEnergyProfile energyProfile) {
	// long st = slackTimeManager.getSlackTime(date, taskset);
	// metrics.put(Event.SlackTime, st);
	// if (st > 0) {
	//
	// long de = energyProfile.getMaxEnergyLevel()
	// - energyProfile.getCurrentEnergyLevel();
	// long dt = de / energyProfile.getPowerRate();
	//
	// if (dt == 0)
	// return null;
	//
	// st = Math.min(st, dt);
	// wakeup = date + st;
	// EventGenerator.generateEvent("check_energy_state", task, wakeup,
	// null);
	// }
	// return null;
	// }
}
